#pragma  once
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <unordered_map>
#include <algorithm>
#include <cstdlib>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/sendfile.h>
#include <sys/wait.h>
#include "log.hpp"
#include "Util.hpp"

#define SEP ": "
#define WEB_ROOT "wwwroot"
#define HOME_PAGE "index.html"
#define HTTP_VERSION "HTTP/1.0"
#define LINE_END "\r\n"
#define PAGE_404 "404.html"


#define OK 200
#define NOT_FOUND 404

//错误码 
#define OK 200
#define BAD_REQUEST 400
#define NOT_FOUND 404
#define SERVER_ERROR 500

static std::string Code2Desc(int code)
{
    std::string desc;
    switch(code){
        case 200:
            desc = "OK";
            break;
        case 404:
            desc = "Not Found";
            break;
        default:
            break;
    }
    return desc;
}

static std::string Suffix2Desc(const std::string &suffix)
{
    static std::unordered_map<std::string, std::string> suffix2desc = {
        {".html", "text/html"},
        {".css", "text/css"},
        {".js", "application/javascript"},
        {".jpg", "application/x-jpg"},
        {".xml", "application/xml"},
    };

    auto iter = suffix2desc.find(suffix);
    if(iter != suffix2desc.end()){
        return iter->second;
    }
    return "text/html";
}

class HttpRequest{
    public:
        std::string request_line;
        std::vector<std::string> request_header;
        std::string blank;
        std::string request_body;

        //解析完毕之后的结果
        std::string method;
        std::string uri; //path?args
        std::string version;

        std::unordered_map<std::string, std::string> header_kv;
        int content_length;
        std::string path;
        std::string suffix;
        std::string query_string;//参数
        bool cgi;
       //只要客户端上传数据就交给CGI程序去执行，并返回
        int size;
    public:
        HttpRequest():content_length(0), cgi(false){}
        ~HttpRequest(){}
};

class HttpResponse{
    public:
        std::string status_line;
        std::vector<std::string> response_header;
        std::string blank;
        std::string response_body;

        int status_code;
        int fd;
    public:
        HttpResponse():blank(LINE_END),status_code(OK),fd(-1){}
        ~HttpResponse(){}
};


//读取请求，分析请求，构建响应
//IO通信
class EndPoint{
    private:
        int sock;
        HttpRequest http_request;
        HttpResponse http_response;
        bool stop;
    private:
        //读取请求行
        bool RecvHttpRequestLine()
        {
            auto &line = http_request.request_line;
            if(Util::ReadLine(sock, line) > 0){
              //去掉请求行
                line.resize(line.size()-1);
                LOG(INFO, http_request.request_line);
            }
            else{
              //读取出错
                stop = true;
            }
            return stop;
        }
        //读取请求报头
        bool RecvHttpRequestHeader()
        {
            std::string line;
            while(true){
                line.clear();
                if(Util::ReadLine(sock, line) <= 0){
                    stop = true;
                    break;
                }
                if(line == "\n"){
                    http_request.blank = line;
                    break;
                }
                //去掉\n
                
                line.resize(line.size()-1);
                http_request.request_header.push_back(line);
                LOG(INFO, line);
            }
            std::cout <<"stop debug: " << stop << std::endl;
            return stop;
        }
        //分析请求行   将读取后的请求行分别拆开
        void ParseHttpRequestLine()
        {
            auto &line = http_request.request_line;
            std::stringstream ss(line);
            ss >> http_request.method >> http_request.uri >> http_request.version;
            auto &method = http_request.method;
            //将方法全部转化为大写
            std::transform(method.begin(), method.end(), method.begin(), ::toupper);
        }
        //分析Http报头
        void ParseHttpRequestHeader()
        {
            std::string key;
            std::string value;
            for(auto &iter : http_request.request_header)
            {
              //"分割读取到的报头字符串"
                if(Util::CutString(iter, key, value, SEP)){
                    http_request.header_kv.insert({key, value});
                }
            }
        }
        
        //是否需要接收正文body POST才需要
        bool IsNeedRecvHttpRequestBody()
        {
            auto &method = http_request.method;
            if(method == "POST"){
                auto &header_kv = http_request.header_kv;
                auto iter = header_kv.find("Content-Length");
                if(iter != header_kv.end()){
                    http_request.content_length = atoi(iter->second.c_str());
                    return true;
                }
                else 
                {
                  LOG(INFO,"没有找到Content-Length字段");
                  return false;
                }
            }
            return false;
        }
        //接收Body
        bool RecvHttpRequestBody()
        {
            if(IsNeedRecvHttpRequestBody()){
                int content_length = http_request.content_length;
                auto &body = http_request.request_body;

                std::cerr<<content_length<<std::endl;
                char ch = 0;
                while(content_length){
                    ssize_t s = recv(sock, &ch, 1, 0);
                    if(s > 0){
                        body.push_back(ch);
                        content_length--;
                    }
                    else{
                        stop = true;
                        break;
                    }
                }

               body= Util::url_decode((char*)body.c_str());
               //BUG 进行解码前大小更大，已经保存了，解码后大小变小了
               auto& content_length1=http_request.content_length;
               content_length1=body.size();
                LOG(INFO, body);
            }
            return stop;
        }
        
        int ProcessCgi()
        {
            int code = OK;
            //父进程数据
            auto &method = http_request.method;
            auto &query_string =  http_request.query_string; //GET
            auto &body_text = http_request.request_body;     //POST
            std::cerr<<"text"<<http_request.request_body<<std::endl;
            auto &bin = http_request.path; //子进程执行的目标程序,一定存在
            std::cerr<<bin<<std::endl;
            int content_length = http_request.content_length;
            std::cerr<<content_length<<std::endl;
            auto &response_body = http_response.response_body;

            std::string query_string_env;
            std::string method_env;
            std::string content_length_env;

            //父进程通过input去读
            int input[2];
            //通过output去写给cgi
            int output[2];

            if(pipe(input) < 0){
                LOG(ERROR, "pipe input error");
                code = SERVER_ERROR;
                return code;
            }
            if(pipe(output) < 0){
                LOG(ERROR, "pipe output error");
                code = SERVER_ERROR;
                return code;
            }

            //创建子进程进行进程替换
            pid_t pid = fork();
            if(pid == 0 ){ //child
                close(input[0]);
                close(output[1]);

                //使用全局性的环境变量将数据给父进程 更高效
                method_env = "METHOD=";
                method_env += method;
                //导出环境变量方法让子进程知道从哪读参数
                putenv((char*)method_env.c_str());
                //替换的子进程如果是GET方法从环境变量读取参数，POST从管道中读取
                if(method == "GET"){
                    query_string_env = "QUERY_STRING=";
                    query_string_env += query_string;
                    //导出传入的参数 
                    putenv((char*)query_string_env.c_str());
                }

                else if(method == "POST"){
                    content_length_env = "CONTENT_LENGTH=";
                    content_length_env += std::to_string(content_length);
                    putenv((char*)content_length_env.c_str());
                }
                else{
                    //Do Nothing
                }

                //input[1]: 写出  -> 1 -> input[1] 
                //output[0]: 读入 -> 0 -> output[0]
                

                dup2(output[0], 0);
                dup2(input[1], 1);

                std::cerr<<"cgi执行的程序:"<<bin<<std::endl;
                //替换请求的path程序
                execl(bin.c_str(), bin.c_str(), nullptr);//替换后文件描述符数组内容012是一样的
                //替换失败直接退出
                exit(1);
            }
            else if(pid < 0){ //error
                LOG(ERROR, "fork error!");
                code = SERVER_ERROR;
                return code;
            }
            else{ 
                //parent
                close(input[1]);
                close(output[0]);
                //如果是GET方法子进程自己导入环境变量
                //如果是POST方法传递body给子进程
                if(method == "POST"){
                    const char *start = body_text.c_str();
                    int total = 0;
                    int size = 0;
                    //父进程将数据拿给替换后的子进程
                    while(total < content_length && (size= write(output[1], start+total, body_text.size()-total)) > 0){
                        total += size;
                    }
                }

                //从0读  子进程退出写端关闭读端也随即关闭
                char ch = 0;
                while(read(input[0], &ch, 1) > 0){
                    response_body.push_back(ch);
                }

                int status = 0;
                pid_t ret = waitpid(pid, &status, 0);
                if(ret == pid){
                    if(WIFEXITED(status)){
                        if(WEXITSTATUS(status) == 0){
                           //正常退出，结果正确 
                            code = OK;
                        }
                        else{
                            //正常退出，结果不正确
                          std::cerr<<"GET请求静态网页并传参，该程序不可执行，直接返回文件内容"<<std::endl;
                         code=BAD_REQUEST; 
                        }
                    }
                    else{
                      //异常退出
                        code = SERVER_ERROR;
                    }
                }

                close(input[0]);
                close(output[1]);
            }
            return code;
        }
        void HandlerError(std::string page)
        {
            std::cout << "debug: " << page << std::endl;
            http_request.cgi = false;
            //要给用户返回对应的404页面
            http_response.fd = open(page.c_str(), O_RDONLY);
            if(http_response.fd > 0){
                struct stat st;
                stat(page.c_str(), &st);
                http_request.size = st.st_size;

                std::string line = "Content-Type: text/html";
                line += LINE_END;
                http_response.response_header.push_back(line);

                line = "Content-Length: ";
                line += std::to_string(st.st_size);
                line += LINE_END;
                http_response.response_header.push_back(line);
            }
        }
        int ProcessNonCgi()
        {
            http_response.fd = open(http_request.path.c_str(), O_RDONLY);
            if(http_response.fd >= 0){
                return OK;
            }
            return NOT_FOUND;
        }
        //构建OK响应
        void BuildOkResponse()
        {
           //构建响应报头
            std::string line = "Content-Type: ";
            line += Suffix2Desc(http_request.suffix);
            line += LINE_END;
            http_response.response_header.push_back(line);
          
            line = "Content-Length: ";
            if(http_request.cgi){
                //CGI正文在response_body
              std::cerr<<"test"<<http_response.response_body<<std::endl;
                line += std::to_string(http_response.response_body.size());
            }
            else{
              //请求文件大小size
                line += std::to_string(http_request.size); //Get非传参
            }
            line += LINE_END;
            http_response.response_header.push_back(line);
        }
        //构建响应
        void BuildHttpResponseHelper()
        {
            auto &code = http_response.status_code;
            //构建状态行
            auto &status_line = http_response.status_line;
            status_line += HTTP_VERSION;
            status_line += " ";
            status_line += std::to_string(code);
            status_line += " ";
            status_line += Code2Desc(code);
            status_line += LINE_END;

            //构建响应正文,可能包括响应报头
            std::string path = WEB_ROOT;
            path += "/";
            switch(code){
                case OK:
                    BuildOkResponse();
                    break;
                case NOT_FOUND:
                    path += PAGE_404;
                    HandlerError(path);
                    break;
                case BAD_REQUEST:
                    path += PAGE_404;
                    HandlerError(path);
                    break;
                case SERVER_ERROR:
                    path += PAGE_404;
                    HandlerError(path);
                    break;
//                case 500:
//                  HandlerError(PAGE_500);
//                    break;
                default:
                    break;
            }
        }
    public:
        EndPoint(int _sock):sock(_sock), stop(false)
        {}
        bool IsStop()
        {
            return stop;
        }
        //接收请求
        void RecvHttpRequest()
        {
            //  读取没错
            if( (!RecvHttpRequestLine()) && (!RecvHttpRequestHeader()) ){
                ParseHttpRequestLine();
                ParseHttpRequestHeader();
                RecvHttpRequestBody();
            }
            
        }
        //构建响应
        void BuildHttpResponse()
        {
            //请求已经全部读完,即可以直接构建响应了
            std::string _path;
            struct stat st;
            std::size_t found = 0;
            auto &code = http_response.status_code;
           
            if(http_request.method != "GET" && http_request.method != "POST"){
                //非法请求
                LOG(WARNING, "method is not right");
                code = BAD_REQUEST;
                goto END;
            }

            //如果是GET方法确认其是否带参，拿到PATH
            if(http_request.method == "GET"){
                size_t pos = http_request.uri.find('?');
              if(pos != std::string::npos){
                    //分离path与参数
                    Util::CutString(http_request.uri, http_request.path, http_request.query_string, "?");
                }
                else{
                    http_request.path = http_request.uri;
                }
            }
            else if(http_request.method == "POST"){
                //POST
                http_request.cgi = true;
                http_request.path = http_request.uri;
            }
            else{
                //Do Nothing
            }
            //转化path为WEB目录
            _path = http_request.path;
            http_request.path = WEB_ROOT;
            http_request.path += _path;
            
            //处理最后一个字符为根目录则默认给首页信息
            if(http_request.path[http_request.path.size()-1] == '/'){
                http_request.path += HOME_PAGE;
            }
            std::cerr<<http_request.path<<std::endl;
            //判断请求资源是否存在
            if(stat(http_request.path.c_str(), &st) == 0){
                if(S_ISDIR(st.st_mode)){
                    //说明请求的资源是一个目录，返回index网页
                    http_request.path += "/";
                    http_request.path += HOME_PAGE;
                    stat(http_request.path.c_str(), &st);
                }
                //请求的资源是可执行程序
                //只要客户端上传数据就交给CGI程序去执行，并返回
                if(  (st.st_mode&S_IXUSR) || (st.st_mode&S_IXGRP) || (st.st_mode&S_IXOTH) ){ //三者中有一个具有权限
                    http_request.cgi = true;
                }

                http_request.size = st.st_size;//获取文件的大小
            }
            else{
                //说明资源是不存在的
                LOG(WARNING, http_request.path + " Not Found");
                code = NOT_FOUND;
                goto END;
            }

            //提取后缀判断文件类型
            found = http_request.path.rfind(".");
            if(found == std::string::npos){
                //默认html
                http_request.suffix = ".html";
            }
            else{
                http_request.suffix = http_request.path.substr(found);
            }
            
            if(http_request.cgi){
                code = ProcessCgi(); //执行目标程序，拿到结果:http_response.response_body;
            }
            else{
                //非CGI GET并且不上传参数并且不是请求的可执行程序
                code = ProcessNonCgi();//GET方法请求静态网页
            }
END:
            BuildHttpResponseHelper(); 
        }
        //发送响应
        void SendHttpResponse()
        {
            //发送状态行
            send(sock, http_response.status_line.c_str(), http_response.status_line.size(), 0);
            //发送报头
            for(auto iter : http_response.response_header){
                send(sock, iter.c_str(), iter.size(), 0);
            }
            //发送空行
            send(sock, http_response.blank.c_str(), http_response.blank.size(), 0);
            
            //发送正文
            
            if(http_request.cgi){
                auto &response_body = http_response.response_body;
                size_t size = 0;
                size_t total = 0;
                const char *start = response_body.c_str();
                while( total < response_body.size() && (size = send(sock, start + total, response_body.size() - total, 0)) > 0)
                {
                    total += size;
                }
            }
            else{
                //直接在内核层发送
                sendfile(sock, http_response.fd, nullptr, http_request.size);//发送文件
                close(http_response.fd);//发送完关闭fd
            }
        }
        ~EndPoint()
        {
            close(sock);
        }
};


class CallBack{
    public:
        CallBack()
        {}
        void operator()(int sock)
        {
            HandlerRequest(sock);
        }
        void HandlerRequest(int sock)
        {
            LOG(INFO, "Hander Request Begin");
#ifdef DEBUG
            //For Test
            char buffer[4096];
            recv(sock, buffer, sizeof(buffer), 0);
            std::cout << "-------------test  begin----------------" << std::endl;
            std::cout << buffer << std::endl;
            std::cout << "-------------test  end----------------" << std::endl;
#else 
            //tcp面向字节流只能一行一行的读防止粘包问题
            EndPoint *ep = new EndPoint(sock);
            ep->RecvHttpRequest();
            if(!ep->IsStop()){ 
              //读取没错才构建响应，发送
                ep->BuildHttpResponse();
                ep->SendHttpResponse();
            }
            else{
                LOG(WARNING, "Recv Error, Stop Build And Send");
            }
            delete ep;
#endif
            LOG(INFO, "-----------Hander Request End------------");
            std::cerr<<std::endl;
        }
        ~CallBack()
        {}
};



